//
// Created by 王乐 on 2022/2/7.
//

#include "LePlayerBehaviour.h"

#include "LeInput.h"

#include "LeRigidBody.h"
#include "LeSpriteRenderer.h"
#include "LeGameObject.h"

#include "LeTime.h"

#include "LeParticleGameObject.h"
#include "LeParticleSystem.h"

#include <iostream>

#include "LeUtils.h"

#include "LeCameraManager.h"

LePlayerBehaviour::LePlayerBehaviour()
{}

LePlayerBehaviour::~LePlayerBehaviour()
{

}

void LePlayerBehaviour::Start()
{
    m_State = STAND;
    m_Orient = PO_RIGHT;
    m_CurJumpNum = 0;
    m_WalkSpeed = 3;
    m_JumpSpeed = 7;
    m_SprintForce = 7;
    m_SprintTime = 0.2;
    m_CurSprintTime = 0;
    m_CurShadowTime = 0;
    m_CurSprintNum = 0;
    m_preSprintState = WALK;
    m_IsInAir = false;

    m_EmitTime = 0.5;
    m_CurEmitTime = 0;

    m_pSpriteRenderer = dynamic_cast<LeSpriteRenderer*>(m_pRenderer);

    m_pSpriteRenderer->changeSpriteSheet("Standing");
    m_pSpriteRenderer->setFlipX(false);
}

void LePlayerBehaviour::Update()
{


}

void LePlayerBehaviour::FixedUpdate()
{
    switch (m_State)
    {
        case STAND:
            processStand();
            break;
        case WALK:
            processWalk();
            break;
        case JUMP:
            processJump();
            break;
        case CLIMB:
            processClimb();
            break;
        case SPRINT:
            processSprint();
            break;
    }
}

void LePlayerBehaviour::processStand()
{
    if(LeInput::getKey(GLFW_KEY_LEFT) == GLFW_PRESS)
    {
        if(m_pRigidBody->m_Body->GetLinearVelocity() != b2Vec2_zero)
            m_pRigidBody->m_Body->SetLinearVelocity(b2Vec2_zero);
        m_pSpriteRenderer->changeSpriteSheet("Walking");
        m_pSpriteRenderer->setFlipX(true);
        m_State = WALK;
        m_Orient = PO_LEFT;
    }
    if(LeInput::getKey(GLFW_KEY_RIGHT) == GLFW_PRESS)
    {
        if(m_pRigidBody->m_Body->GetLinearVelocity() != b2Vec2_zero)
            m_pRigidBody->m_Body->SetLinearVelocity(b2Vec2_zero);
        m_pSpriteRenderer->changeSpriteSheet("Walking");
        m_pSpriteRenderer->setFlipX(false);
        m_State = WALK;
        m_Orient = PO_RIGHT;
    }
    if(LeInput::getKey(GLFW_KEY_SPACE) == GLFW_PRESS)
    {
        m_pSpriteRenderer->changeSpriteSheet("Jumping");
        b2Vec2 curvelocity = m_pRigidBody->m_Body->GetLinearVelocity();
        m_pRigidBody->m_Body->SetLinearVelocity(b2Vec2(curvelocity.x, m_JumpSpeed));
        m_State = JUMP;

        LeParticle partDef(std::string("MyDust"), 0.5, glm::vec3(0,0.3,0), 0.0, -0.1,
                           "/Users/wangle/CLionProjects/Leste/res/dust.png");
        m_pParticleSystem->emitParticles(20, partDef,
                                         glm::vec3(0, -0.5, 0.1),
                                         glm::vec3(-0.2, -0.09, 0), glm::vec3(0.2, 0.09, 0),
                                         glm::vec2(0, 0),
                                         glm::vec3(0.1, 0.1, 0.1), glm::vec3(0.1, 0.1, 0.1));
    }
}

void LePlayerBehaviour::processWalk()
{
    if(LeInput::getKey(GLFW_KEY_LEFT) == GLFW_PRESS)
    {
        if(m_Orient != PO_LEFT)
            m_pRigidBody->m_Body->SetLinearVelocity(b2Vec2_zero);
        b2Vec2 curvelocity = m_pRigidBody->m_Body->GetLinearVelocity();
        m_pRigidBody->m_Body->SetLinearVelocity(b2Vec2(-m_WalkSpeed, curvelocity.y));
        m_pSpriteRenderer->setFlipX(true);
        m_Orient = PO_LEFT;
    }
    if(LeInput::getKey(GLFW_KEY_RIGHT) == GLFW_PRESS)
    {
        if(m_Orient != PO_RIGHT)
            m_pRigidBody->m_Body->SetLinearVelocity(b2Vec2_zero);
        b2Vec2 curvelocity = m_pRigidBody->m_Body->GetLinearVelocity();
        m_pRigidBody->m_Body->SetLinearVelocity(b2Vec2(m_WalkSpeed, curvelocity.y));
        m_pSpriteRenderer->setFlipX(false);
        m_Orient = PO_RIGHT;
    }

    if(LeInput::getKey(GLFW_KEY_LEFT) == GLFW_RELEASE && LeInput::getKey(GLFW_KEY_RIGHT) == GLFW_RELEASE)
    {
        m_pSpriteRenderer->changeSpriteSheet("Standing");
        m_pRigidBody->m_Body->SetLinearVelocity(b2Vec2_zero);
        m_State = STAND;
    }
    if(LeInput::getKey(GLFW_KEY_SPACE) == GLFW_PRESS)
    {
        m_pSpriteRenderer->changeSpriteSheet("Jumping");
        b2Vec2 curvelocity = m_pRigidBody->m_Body->GetLinearVelocity();
        m_pRigidBody->m_Body->SetLinearVelocity(b2Vec2(curvelocity.x, m_JumpSpeed));
        m_State = JUMP;
        LeParticle partDef(std::string("MyDust"), 0.5, glm::vec3(0,0.3,0), 0.0, -0.1,
                           "/Users/wangle/CLionProjects/Leste/res/dust.png");
        m_pParticleSystem->emitParticles(20, partDef,
                                         glm::vec3(0, -0.5, 0.1),
                                         glm::vec3(-0.2, -0.09, 0), glm::vec3(0.2, 0.09, 0),
                                         glm::vec2(0, 0),
                                         glm::vec3(0.1, 0.1, 0.1), glm::vec3(0.1, 0.1, 0.1));
    }
    if(LeInput::getKey(GLFW_KEY_S) == GLFW_PRESS && m_CurSprintNum == 0)
    {
        b2Vec2 horiForce = b2Vec2_zero;
        b2Vec2 vertForce = b2Vec2_zero;
        if(LeInput::getKey(GLFW_KEY_RIGHT) == GLFW_PRESS)
            horiForce = b2Vec2(1, 0);
        else if(LeInput::getKey(GLFW_KEY_LEFT) == GLFW_PRESS)
            horiForce = b2Vec2(-1, 0);
        if(LeInput::getKey(GLFW_KEY_UP) == GLFW_PRESS)
            vertForce = b2Vec2(0, 1);
        else if(LeInput::getKey(GLFW_KEY_DOWN) == GLFW_PRESS)
            vertForce = b2Vec2(0, -1);
        b2Vec2 sprintForce = horiForce + vertForce;
        sprintForce.Normalize();
        m_pRigidBody->m_Body->ApplyLinearImpulse(b2Vec2(sprintForce.x * m_SprintForce, sprintForce.y * m_SprintForce),
                                                 m_pRigidBody->m_Body->GetPosition(), true);
        m_pRigidBody->m_Body->SetGravityScale(0);
        m_CurSprintNum++;
        m_preSprintState = WALK;
        m_State = SPRINT;

        b2Vec2 curVelocity = m_pRigidBody->m_Body->GetLinearVelocity();
        LeParticle partDef(std::string("MyDust"), 0.5, glm::vec3(-curVelocity.x * 0.05,0.3,0), 0.0, -0.1,
                           "/Users/wangle/CLionProjects/Leste/res/dust.png");
        m_pParticleSystem->emitParticles(20, partDef,
                                         glm::vec3(0, -0.5, 0.1),
                                         glm::vec3(-0.2, -0.09, 0), glm::vec3(0.2, 0.09, 0),
                                         glm::vec2(0, 0),
                                         glm::vec3(0.1, 0.1, 0.1), glm::vec3(0.1, 0.1, 0.1));

        LeCameraManager::setCameraShakeMode(true);
    }
}

void LePlayerBehaviour::processJump()
{
    if(LeInput::getKey(GLFW_KEY_LEFT) == GLFW_PRESS)
    {
        if(m_Orient != PO_LEFT)
            m_pRigidBody->m_Body->SetLinearVelocity(b2Vec2_zero);
        b2Vec2 curvelocity = m_pRigidBody->m_Body->GetLinearVelocity();
        m_pRigidBody->m_Body->SetLinearVelocity(b2Vec2(-m_WalkSpeed * 0.8f, curvelocity.y));
        m_pSpriteRenderer->setFlipX(true);
        m_Orient = PO_LEFT;
    }
    if(LeInput::getKey(GLFW_KEY_RIGHT) == GLFW_PRESS)
    {
        if(m_Orient != PO_RIGHT)
            m_pRigidBody->m_Body->SetLinearVelocity(b2Vec2_zero);
        b2Vec2 curvelocity = m_pRigidBody->m_Body->GetLinearVelocity();
        m_pRigidBody->m_Body->SetLinearVelocity(b2Vec2(m_WalkSpeed* 0.8f, curvelocity.y));
        m_pSpriteRenderer->setFlipX(false);
        m_Orient = PO_RIGHT;
    }

    b2Vec2 velocity = m_pRigidBody->m_Body->GetLinearVelocity();
    float yspeed = velocity.y < 0 ? 0 : velocity.y;
    float maxyspeed = 7.55f;
    float scale = 1.f + (0.7f - 1.05f) / maxyspeed * yspeed;
    m_pTransform->setScale(scale, m_pTransform->m_Scale.y, m_pTransform->m_Scale.z);

    if(abs(velocity.y) < 0.5f)
        m_pRigidBody->m_Body->SetGravityScale(0.5f);
    else
        m_pRigidBody->m_Body->SetGravityScale(1.0f);

    switch (m_CurJumpNum)
    {
        case 0:
            if(LeInput::getKey(GLFW_KEY_SPACE) == GLFW_RELEASE)
            {
                m_CurJumpNum++;
            }
            break;
        case 1:
            if(LeInput::getKey(GLFW_KEY_SPACE) == GLFW_PRESS)
            {
                b2Vec2 curvelocity = m_pRigidBody->m_Body->GetLinearVelocity();
                m_pRigidBody->m_Body->SetLinearVelocity(b2Vec2(curvelocity.x, m_JumpSpeed));
                m_CurJumpNum++;
                LeParticle partDef(std::string("MyDust"), 0.5, glm::vec3(0,0.3,0), 0.0, -0.1,
                                   "/Users/wangle/CLionProjects/Leste/res/dust.png");
                m_pParticleSystem->emitParticles(20, partDef,
                                                 glm::vec3(0, -0.5, 0.1),
                                                 glm::vec3(-0.2, -0.09, 0), glm::vec3(0.2, 0.09, 0),
                                                 glm::vec2(0, 0),
                                                 glm::vec3(0.1, 0.1, 0.1), glm::vec3(0.1, 0.1, 0.1));
            }
            break;
        case 2:break;
    }

    if(LeInput::getKey(GLFW_KEY_S) == GLFW_PRESS && m_CurSprintNum == 0)
    {
        m_pRigidBody->m_Body->SetLinearVelocity(b2Vec2_zero);
        b2Vec2 horiForce = b2Vec2_zero;
        b2Vec2 vertForce = b2Vec2_zero;
        if(LeInput::getKey(GLFW_KEY_RIGHT) == GLFW_PRESS)
            horiForce = b2Vec2(1, 0);
        else if(LeInput::getKey(GLFW_KEY_LEFT) == GLFW_PRESS)
            horiForce = b2Vec2(-1, 0);
        if(LeInput::getKey(GLFW_KEY_UP) == GLFW_PRESS)
            vertForce = b2Vec2(0, 1);
        else if(LeInput::getKey(GLFW_KEY_DOWN) == GLFW_PRESS)
            vertForce = b2Vec2(0, -1);
        b2Vec2 sprintForce = horiForce + vertForce;
        sprintForce.Normalize();
        m_pRigidBody->m_Body->ApplyLinearImpulse(b2Vec2(sprintForce.x * m_SprintForce, sprintForce.y * m_SprintForce),
                                                 m_pRigidBody->m_Body->GetPosition(), true);
        m_pRigidBody->m_Body->SetGravityScale(0);
        m_CurSprintNum++;
        m_preSprintState = JUMP;
        m_State = SPRINT;

        LeParticle partDef(std::string("MyDust"), 0.5, glm::vec3(0,0.3,0), 0.0, -0.1,
                           "/Users/wangle/CLionProjects/Leste/res/dust.png");
        m_pParticleSystem->emitParticles(20, partDef,
                                         glm::vec3(0, -0.5, 0.1),
                                         glm::vec3(-0.2, -0.09, 0), glm::vec3(0.2, 0.09, 0),
                                         glm::vec2(0, 0),
                                         glm::vec3(0.1, 0.1, 0.1), glm::vec3(0.1, 0.1, 0.1));

        LeCameraManager::setCameraShakeMode(true);
    }
}

void LePlayerBehaviour::processClimb()
{

}

void LePlayerBehaviour::processSprint()
{
    m_CurSprintTime += LeTime::dDeltaTime;
    m_pTransform->m_Scale.y = LeMath::lerpFloat(m_pTransform->m_Scale.y, 0.6, 0.1);
    m_pTransform->setScale(m_pTransform->m_Scale.x, m_pTransform->m_Scale.y, m_pTransform->m_Scale.z);


    m_CurShadowTime += LeTime::dDeltaTime;
    if(m_CurShadowTime > m_SprintTime / 4)
    {
        m_CurShadowTime = 0;
        LeParticle partDef(std::string("MyShadow"), 0.5, glm::vec3(0,0,0), 0.0, 0,
                           "/Users/wangle/CLionProjects/Leste/res/player_jump.png");
        if(m_Orient == PO_LEFT)
            partDef.m_StartScale = glm::vec3(-1, 1, 1);
        else
            partDef.m_StartScale = glm::vec3(1, 1, 1);
        m_pParticleSystem->emitShadows(partDef);
    }

    LeParticle partDef(std::string("MyDust"), 0.5, glm::vec3(0,0.1,0), 0.0, -0.1,
                       "/Users/wangle/CLionProjects/Leste/res/dust.png");
    int dice = LeMath::getRandomI(1, 100);
    if(dice < 80)
        m_pParticleSystem->emitParticles(1, partDef,
                                     glm::vec3(0, 0, -0.1),
                                     glm::vec3(-0.05, -0.05, 0), glm::vec3(0.05, 0.05, 0),
                                     glm::vec2(0, 0),
                                     glm::vec3(0.1, 0.1, 0.1), glm::vec3(0.1, 0.1, 0.1));


    if(m_CurSprintTime > m_SprintTime)
    {
        m_pRigidBody->m_Body->SetLinearVelocity(b2Vec2_zero);
        m_pTransform->setScale(m_pTransform->m_Scale.x, 1.0f, m_pTransform->m_Scale.z);
        m_CurSprintTime = 0;
        m_pRigidBody->m_Body->SetGravityScale(1);

        if(m_IsInAir)
            m_State = JUMP;
        else
        {
            m_CurSprintNum = 0;
            m_State = WALK;
        }
    }
}

void LePlayerBehaviour::OnContactBegin(LeGameObject *other)
{
    m_IsInAir = false;
    if(other->strName == "MyGround" && m_State != STAND)
    {
        b2Vec2 curVelocity = m_pRigidBody->m_Body->GetLinearVelocity();

        m_pSpriteRenderer->changeSpriteSheet("Standing");
        m_State = STAND;
        m_CurJumpNum = 0;
        m_pRigidBody->m_Body->SetLinearVelocity(b2Vec2_zero);
        m_pTransform->setScale(1.0f, 1.0f, m_pTransform->m_Scale.z);
        m_CurSprintTime = 0;
        m_CurSprintNum = 0;

        LeParticle partDef(std::string("MyDust"), 0.5, glm::vec3(curVelocity.x * 0.3,0.3,0), 0.0, -0.1,
                           "/Users/wangle/CLionProjects/Leste/res/dust.png");
        m_pParticleSystem->emitParticles(20, partDef,
                                         glm::vec3(0, -0.5, 0.1),
                                         glm::vec3(-0.2, -0.09, 0), glm::vec3(0.2, 0.09, 0),
                                         glm::vec2(0, 0),
                                         glm::vec3(0.1, 0.1, 0.1), glm::vec3(0.1, 0.1, 0.1));
    }
}

void LePlayerBehaviour::OnContactEnd(LeGameObject *other)
{
    m_IsInAir = true;
}
